﻿#!/usr/bin/env python
#_*_encoding:utf-8_*_ 
from head import *
import copy,sys

class TGBms:
    #head field
    player          = None
    genre           = None
    title           = None
    artist          = None
    playlevel       = None
    bpm             = None
    rank            = None
    wav             = None
    id              = None
    roundgap        = '16'

obj = TGBms()       #当前bms对应的实例
gAllSec = []        #所有节,根据节号归类
gRoundSection = []  #所有轮对应的节
gAllround = []      #所有轮对象列表
gBreakPoint = {}    #需要加空轮的标记,key是轮号，value是表示插几个空轮
gBalloon = {}       #key=roundIndex,value是个串，包含了这个键
gIndex = 0  #标识有效的节号，因为有的节可能是标识信息的特殊节
gLongKey = enmKeyValue_Any | enmKeyValue_LTriggle       #长按键的键名

def LoadTGBMS(name):
    global obj,gAllSec
    Line = []
    f = open(name)
    for item in f:
        item = item.strip()
        if item:
            Line.append(item)
    f.close()
    datafield = []
    for i in xrange(len(Line)):
        p = Line[i].split(' ')

        if len(p) == 2:
            #head field
            if p[0] == '#PLAYER':
                obj.player = p[1]
                continue
        
            elif p[0] == '#GENRE':
                obj.genre = p[1]
                continue
        
            elif p[0] == '#TITLE':
                obj.title = p[1].decode('gbk').encode('utf-8')
                continue

            elif p[0] == '#ARTIST':
                obj.artist = p[1]
                continue

            elif p[0] == '#BPM':
                obj.bpm = p[1]
                continue

            elif p[0] == '#PLAYLEVEL':
                obj.playlevel = p[1]
                continue

            elif p[0] == '#RANK':
                obj.rank = p[1]
                continue

            elif p[0] == '#WAV02':
                obj.wav = p[1]
                continue
            
        if len(p) == 1 and len(Line[i].split(':')) ==2:
            #data field
            datafield.append(Line[i])

    seclist = ProcessTG(datafield)
    gAllSec = copy.copy(seclist)
    for i in xrange(len(seclist)):
        if len(seclist)-1 == i:
            pass
        Create(seclist[i])

def ProcessTG(data):
    #将每一节归入一个list,节号相同的是一节
    sectionlist = []
    length = len(data)
    curSection = []
    j = 0
    for i in xrange(length):
        sectionNum = int(data[i].split(':')[0][1:])/100

        if int(sectionNum) == 0:
            continue
        
        if sectionNum != int(data[i-1].split(':')[0][1:])/100 and j!=0:
            sectionlist.append(curSection)
            curSection = []
        else:
            #走到这，说明是该节的行
            pass
        curSection.append(data[i][1:])
        j += 1
        if i== length-1:
            #最后一行，因为没有下一节了，所以强行dump
            sectionlist.append(curSection)
    return sectionlist

class RoundTG():
    '''
    rule like 0x00000000
    每个rule有8位，可表示四个键，表示一个半小节，一整小节最多可放8键,那么一节也就是4拍最多放32键
    若以后还要更多的键，再扩展这个rule长度
    '''
    def __init__(self):
        self.ft = '1'
        #self.IsNeedEmpty = 0
        self.rule = {0:0,1:0,2:0,3:0,4:0,5:0,6:0,7:0}
        self.sound={0:'',1:'',2:'',3:'',4:'',5:'',6:'',7:''}
        self.long = {}  #长按键信息,key是键值,value是键位，整数0~64
        self.nCount = 0     #长按键个数

def Create(data):
    global gAllround,gIndex
    num = TGKeyNum*2
    if len(data) ==1 and data[0].split(':')[0][-2:] == '16' and data[0].split(':')[1][0] == 'A':
        #如果是插空轮的标记，直接跳过
        gBreakPoint[len(gAllround)] = int(data[0].split(':')[1][1])
        return
    if gIndex%2 == 0:
        thisround = RoundTG()
        gAllround.append(thisround)
    else:
        pass
    l = len(gAllround)
    thisround = gAllround[l-1]
    keydic = {}
    for item in data:
        citem = Convert(item)
        p = citem.split(':')
        key = int(p[0])%100
        body = p[1]
        keydic[key] = body

        #修正本轮fevertime
        if key == 16 and item.split(':')[1][0:2] == '07':
            thisround.ft = '2'
        if key == 16 and item.split(':')[1][0] == 'A':
            gBreakPoint[len(gAllround)] = int(data[0].split(':')[1][1])
            return

    for k in keydic.keys():
        body = keydic[k]
        #length = len(body)
        key = StrToKey(k)
        if not key:
            #跳过长按键，及16号按键,后面处理
            continue
        pos = gIndex%2
        AsignKeyByPos(key,body,thisround,pos, num, enmGameMode_TG)
    gIndex += 1

def StrToKey(s):
    if s == 11:
        return enmKeyValue_LEFT
    elif s == 12:
        return enmKeyValue_UP
    elif s == 13:
        return enmKeyValue_DOWN
    elif s == 14:
        return enmKeyValue_RIGHT
    elif s == 15:
        return enmKeyValue_SPACE
    else:
        return enmKeyValue_None


#RoundSection和YL不同，这个里面不含有长按键
def SetRoundSection():
    global gAllSec, gRoundSection
    i = 0
    j = 0
    cur = []
    
    for item in gAllSec:        
        if len(item)==1 and item[0].split(':')[0][-2:]=='16':
            #在这里已经跳过了插空轮的标记
            j += 1
            continue
        else:
            cur.append(item)
        if i%2 == 1:
            gRoundSection.append(cur)
            cur = []
        i += 1
        j += 1
        if j== len(gAllSec):
            gRoundSection.append(cur)
        
def AddSound():
    global gAllround,gRoundSection
    SetRoundSection()
    for i in xrange(len(gRoundSection)):
        if i == len(gRoundSection)-1:
            pass
        thisround = gAllround[i]
        GetSound(thisround, i)

def GetSound(thisround,i):
    global gAllSec, gRoundSection
    thisroundsection = gRoundSection[i]
    index = 0
    num = TGKeyNum*2    #位数
    for sec in thisroundsection:
        for item in sec:
            flag = False    #测试该串是否含有气球键
            key = item.split(':')[0][-2:]
            if key=='16':
                #跳过标志键
                continue
            body = item.split(':')[1]
            flag, s = AlignVoice(body, 64)
            if flag:
                #气球键，音效不给，有特殊音效
                pass
            else:
                for i in xrange(4):
                    #一轮4rule
                    r = i
                    if index == 1:
                        r += 4
                    body = s[num*i:(i+1)*num]
                    for j in xrange(len(body)/2):
                        v = body[j*2:(j+1)*2]
                        if v != '00':
                            thisround.sound[r] = v+thisround.sound[r]
                        else:
                            pass
        index += 1   
        
        
def SaveTG(name):
    if name.split('.')[1].lower() != 'bms':
        return False
    
    LoadTGBMS(name)
    AddSound()
    Rule2String()
    PickLongPress()
    ModifyByLongInfo()
    PickBalloon()
    AppendBalloon()
        
    doc = Document()
    root = doc.createElement('musics')
    doc.appendChild(root)

    music = doc.createElement('music')
    music.setAttribute('id',name.split('.')[0])
    music.setAttribute('level',obj.playlevel)
    music.setAttribute('roundgap','8')
    music.setAttribute('gridcount','8')
    root.appendChild(music)
    i = 0
    for item in gAllround:
        #print '%d:%s'%(i,item.start)
        round = doc.createElement('round')
        round.setAttribute('type',item.ft)
        
        index = 0
        for k in item.rule.keys():
            rule = item.rule[k]
            sound = item.sound[k]
            if not rule:
                rule = ''
            if not sound:
                sound = ''
            else:
                sound = '0x'+sound
            round.setAttribute('rule%d'%(index+1),rule)
            round.setAttribute('sound%d'%(index+1),sound)
            index += 1
        music.appendChild(round)
        #轮号加1
        i += 1
        
        num = gBreakPoint.get(i)
        if num!=None and num!=0:
            #说明在这一节有空轮，先加空轮
            for j in xrange(num):
                emptyround = doc.createElement('round')
                emptyround.setAttribute('type','3')
                music.appendChild(emptyround)
                i += 1

    s = doc.toprettyxml(indent='    ',encoding='UTF-8')
    if name[0] == '3':
        folder = 'TG'
    elif name[0] == '4':
        folder = 'YL'

    if not MACRO_DEBUG:
        f = open('..'+os.sep+folder+os.sep+name.split('.')[0]+'.xml','w')
    else:
        f = open(name.split('.')[0]+'.xml','w')
    f.write(s)
    f.close()
    return True

def Rule2String():
    global gAllround
    for item in gAllround:
        for k in item.rule.keys():
            item.rule[k] = int2hex(item.rule[k])


#太鼓的长条，只会出现在55上，而且长条是那种狂按的，并且是任意键,长按键信息放到轮的类中去
#因为有可能一轮有会有多次长按键
def PickLongPress():
    global gRoundSection, gAllround
    roundindex = 0
    LPress = False
    for item in gRoundSection:
        #扫描前半轮中的节是否有长按
        thisround = gAllround[roundindex]
        for line in item[0]:
            body = line.split(':')[1]
            key = line.split(':')[0][-2:]
            if key=='55' and int(body)!=0:
                body = Align(body, 64)
                for i in xrange(len(body)/2):
                    k = body[i*2:(i+1)*2]
                    if k == '00':
                        continue
                    else:
                        if not LPress:
                            #长按没有按，start
                            thisround.long[thisround.nCount] = 's:%d'%i
                            thisround.nCount += 1
                            LPress = True
                        elif LPress and thisround.long.get(thisround.nCount-1) != None:
                            #end
                            thisround.long[thisround.nCount-1] += ',e:%d'%i
                            LPress = False
                        elif LPress and len(thisround.long) == 0:       #因为现在长按是只有一种键，这样可以判断出本轮未开始，但是有结束，说明在前面的轮开始的
                            #end
                            thisround.long[thisround.nCount] = 'e:%d'%i
                            thisround.nCount += 1
                            LPress = False
                            
        #后半轮
        if len(item) == 2:
            #保护一下，防止最后一轮只有前半轮
            for line in item[1]:
                body = line.split(':')[1]
                key = line.split(':')[0][-2:]
                if key=='55' and int(body)!=0:
                    body = Align(body, 64)
                    for i in xrange(len(body)/2):
                        k = body[i*2:(i+1)*2]
                        if k == '00':
                            continue
                        else:
                            if not LPress:
                                #长按没有按，start
                                thisround.long[thisround.nCount] = 's:%d'%(i+32)
                                thisround.nCount += 1
                                LPress = True
                            elif LPress and thisround.long.get(thisround.nCount-1) != None:
                                #end
                                thisround.long[thisround.nCount-1] += ',e:%d'%(i+32)
                                LPress = False
                            elif LPress and len(thisround.long) == 0:       #因为现在长按是只有一种键，这样可以判断出本轮未开始，但是有结束，说明在前面的轮开始的
                                #end
                                thisround.long[thisround.nCount] = 'e:%d'%(i+32)
                                thisround.nCount += 1
                                LPress = False
        
        if len(thisround.long) == 0 and LPress:
            #全部长按，没有在本轮开始，也没有在本轮结束
            thisround.long[thisround.nCount] = 'ALong'
        roundindex += 1

def ModifyByLongInfo():
    global gAllround,gLongKey
    for this in gAllround:
        if len(this.long) == 0:
            #没有空轮信息
            continue
        
        for key in this.long.keys():
            p = this.long[key].split(',')
            l = len(p)
            if l==1:
                #本轮只有结束或者开始
                flag = p[0].split(':')[0]
                value = int(p[0].split(':')[1])
                r = value/8
                pos  = value%8
                #原来的键
                oldvalue = this.rule[r]
                if oldvalue == '':
                    oldvalue = '0x00'
                oldvalue = int(oldvalue, 16)
                if flag == 's':
                    #开始
                    #整轮的
                    for i in xrange(8):
                        if i>r:
                            keySerial = 0
                            for j in xrange(8):
                                keySerial |= gLongKey << 8*j
                            this.rule[i] = int2hex(keySerial)
                    
                    #如果长按键没在整拍上开始
                    if pos != 0:
                        keySerial = 0
                        for j in xrange(8-pos):
                            keySerial |= gLongKey << 8*(pos+j)
                        keySerial |= oldvalue
                        this.rule[r] = int2hex(keySerial)
                            
                elif flag == 'e':
                    #结束
                    for i in xrange(8):
                        if i<r:
                            keySerial = 0
                            for j in xrange(8):
                                keySerial |= gLongKey << 8*j
                            this.rule[i] = int2hex(keySerial)
                    #如果结束不是整拍
                    if pos != 0:
                        keySerial = 0
                        for j in xrange(8-pos):
                            keySerial |= gLongKey << 8*j
                        keySerial |= oldvalue
                        this.rule[r] = int2hex(keySerial)
                elif flag == 'ALong':
                    #整轮长按
                    longValue = 0
                    for i in xrange(8):
                        longValue |= gLongKey<<8*i
                    for i in xrange(8):
                        this.rule[i] = int2hex(longValue)
                            
            if l==2:
                #有始有终
                s = int(p[0].split(':')[1])
                e = int(p[1].split(':')[1])
                sr = s/8    #开始rule
                sp = s%8    #开始键位
                er = e/8    #结束rule
                ep = e%8    #结束键位
                oldstart = this.rule[sr]
                oldend   = this.rule[er]
                if oldstart == '':
                    oldstart ='0x00'
                if oldend == '':
                    oldend = '0x00'
                oldstart = int(oldstart, 16)
                oldend   = int(oldend, 16)
                
                
                for i in xrange(8):
                    if i>sr and i<er:
                        keySerial = 0
                        for j in xrange(8):
                            keySerial |= gLongKey << 8*j
                        this.rule[i] = int2hex(keySerial)
                
                if sp != 0:
                    keySerial = 0
                    for j in xrange(8-sp):
                        keySerial |= gLongKey << 8*(sp+j)
                    keySerial |= oldstart
                    this.rule[sr] = int2hex(keySerial)
                
                if ep != 0:
                    keySerial = 0
                    for j in xrange(8-ep):
                        keySerial |= gLongKey << 8*j
                    keySerial |= oldend
                    this.rule[ep] = int2hex(keySerial)

def GetBalloonHead(i):
    if i=='4':
        return enmKeyValue_Balloon4
    elif i=='6':
        return enmKeyValue_Balloon6
    elif i=='8':
        return enmKeyValue_Balloon8
    elif i=='10':
        return enmKeyValue_Balloon10
    elif i=='12':
        return enmKeyValue_Balloon12
    return enmKeyValue_LNormal

#收集气球键
def PickBalloon():
    global gAllSec, gBalloon
    roundIndex = 0
    i = 0
    for item in gAllSec:        
        if len(item)==1 and item[0].split(':')[0][-2:]=='16':
            #在这里已经跳过了插空轮的标记
            continue
        else:
            for ele in item:
                string = ele.split(':')[1]
                if 'B' in string:
                    #说明这个是气球键
                    k = (roundIndex, i%2)   #第二个元素0是前半轮,1是后半轮
                    gBalloon[k] = ele
                    continue
        if i%2 == 1:
            #每轮第二节时才增长roundIndex
            roundIndex += 1
        i += 1
        
#特殊处理气球键，因为气球按键是变化的
def AppendBalloon():
    global gBalloon,gAllround
    num = TGKeyNum*2
    for key in gBalloon:
        roundIndex,pos = key
        p = gBalloon[key].split(':')
        key = int(p[0])%100
        body = AlignBalloon(p[1],64)
        round = gAllround[roundIndex]
        for i in xrange(len(body)/num):
            #r是rule号
            r = 0
            if pos == 0:
                #前半轮
                r = i
            else:
                #后半轮
                r = 4+i
            ele = body[num*i:(i+1)*num]
            for j in xrange(TGKeyNum):
                #j是键号
                value = ele[j*2:(j+1)*2]
                if value[0] == 'B':
                    BalloonKey = GetBalloonHead(value[1])|StrToKey(key)
                    keyvalue = int(round.rule[r],16)
                    keyvalue |= BalloonKey<<j*8
                    round.rule[r] = int2hex(keyvalue)
                else:
                    pass
        

if __name__ == '__main__':
    SaveTG('30001.bms')

